﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.Exchange.WebServices.Data;
using Microsoft.Crm.Sdk.Messages;
using System.Threading;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Client;
using Microsoft.Xrm.Sdk.Query;
using Microsoft.Xrm.Sdk.Messages;
using Microsoft.Xrm.Sdk.Metadata;

namespace Email_Validator
{
    /// <summary>
    /// Interaction logic for ValidatorWindow.xaml
    /// </summary>
    public partial class ValidatorWindow : Window
    {

        private crmauthinfo _crmAuth;

        public crmauthinfo CrmAuth
        {
            get { return _crmAuth; }
            set { _crmAuth = value; }
        }
        
        private exchangeauthinfo _exchangeAuth;

        public exchangeauthinfo ExchangeAuth
        {
            get { return _exchangeAuth; }
            set { _exchangeAuth = value; }
        }

        public OptionSetValue ConnectionChannel
        {
            get { return _connectionChannel; }
            set { _connectionChannel = value; }
        }

        private OptionSetValue _connectionChannel;

       public OptionSetValue DeliveryMethod
        {
            get { return _deliveryMethod; }
            set { _deliveryMethod = value; }
        }

        private OptionSetValue _deliveryMethod;

        public string DeliveryMethodString
        {
            get { return _deliveryMethodString; }
            set { _deliveryMethodString = value; }
        }

        private string _deliveryMethodString;

        public string FilteringMethod
        {
            get { return _filteringMethod; }
            set { _filteringMethod = value; }
        }

        private string _filteringMethod;

        public Exception Exception
        {
            get { return _exception; }
            set { _exception = value; }
        }

        private Exception _exception;

        private Entity _mailbox;
        private Entity _userSetting;
        private Entity _queue;
        private Entity _organization;
        private Entity _emailServerProfile;

        private string _connectedExchangeEmail;

        public ValidatorWindow(crmauthinfo CrmAuthenticationInfo, exchangeauthinfo ExchangeAuthenticationInfo)
        {
            CrmAuth = CrmAuthenticationInfo;
            ExchangeAuth = ExchangeAuthenticationInfo;
            InitializeComponent();
            Thread infoworkerthread = new Thread(new ThreadStart(PrepareInfo));
            infoworkerthread.SetApartmentState(ApartmentState.STA);
            infoworkerthread.Start();
        }

        private void PrepareInfo()
        {
            try
            {
                CrmAuth.GetOrgProxy(CrmAuth.OrgDetail);
                _organization = GetOrganization(CrmAuth);
                _connectedExchangeEmail = GetExchangeEmailAddress(ExchangeAuth);
                
               
                if (CrmAuth.IsQueue)
                {
                    _mailbox = GetMailbox(CrmAuth.TargetMailbox);
                    _queue = GetQueueSettings(_mailbox);
                    SetPropertyValuesFromMetadata(_mailbox, _organization, _queue);
                }
                else
                {
                   
                    Guid userId = GetUserId(CrmAuth.CrmService, CrmAuth.TargetMailbox);
                    _mailbox = GetMailbox(userId);
                    _userSetting = GetUserSettings(CrmAuth.CrmService, userId);
                    SetPropertyValuesFromMetadata(_mailbox, _organization, _userSetting);
                }

                if (_mailbox.Attributes.ContainsKey("emailserverprofile"))
                {
                    _emailServerProfile = GetEmailServerProfile();
                }

                this.Dispatcher.Invoke((Action)(() =>
                {
                    mailboxvalueLb.Content = _mailbox["emailaddress"];
                    autopromotevalueLbl.Content = FilteringMethod;
                    deliverymethodvalueLbl.Content = DeliveryMethodString;
                    organizationvalueLbl.Content = CrmAuth.OrgDetail.FriendlyName;
                    if (_organization.Contains("enablesmartmatching"))
                    {
                        smartmatchinvalueLbl.Content = "Enabled";
                    }
                    else
                    {
                        smartmatchinvalueLbl.Content = "Disabled";
                    }
                    if ((int)_organization["maximumtrackingnumber"] == 0)
                    {
                        trackingtokenvalueLbl.Content = "Disabled";
                    }
                    else
                    {
                        trackingtokenvalueLbl.Content = "Enabled";
                    }

                    lblExchangeMailbox.Content = lblExchangeMailbox.Content + _connectedExchangeEmail;
                    btnDiagnostic.IsEnabled = true;

                }));
               
            }

            catch (Exception ex)
            {
                this.Dispatcher.Invoke((Action)(() =>
                {
                    SetErrorStatus();
                }));
                Exception = ex;
                MessageBox.Show(MainWindow.GetErrorDetail(ex), "Exception during PrepareInfo()");
            }
        }

        private Guid GetUserId(OrganizationServiceProxy crmService, string EmailAddress)
        {
            QueryByAttribute query = new QueryByAttribute("systemuser");
            query.ColumnSet = new ColumnSet();
            query.Attributes.AddRange("internalemailaddress");
            query.Values.AddRange(EmailAddress);
            query.Attributes.AddRange("isdisabled");
            query.Values.AddRange(false);

            EntityCollection users = CrmAuth.CrmService.RetrieveMultiple(query);

            if (users.Entities.Count > 1)
            {
                throw new Exception("Found multiple users associated with the target mailbox email address. Please check the configuration and try again.");
            }

            if (users.Entities.Count == 0)
            {
                throw new Exception("Did not find any users associated with the target mailbox email address. Please check the configuration and try again.");
            }

            Entity user = users[0];
            return user.Id;
        }

        //This method will return the e-mail address of the mailbox we are connected to Exchange. 
        //This method is static to allow consumption across classes.
        //To-Do: Implement an enumeration for scenarios where usedefaultcredentials or the address isn't found, which can be checked by callers.
        static string GetExchangeEmailAddress(exchangeauthinfo ExAuth)
        {
            //Exchange On Premise - UseDefaultCredentials
            //To-Do: Determine how we can fetch the connected email address when using default credentials
            //Does Exchange store this in the Username and Password properties in the background?
            if (ExAuth.EwsService.UseDefaultCredentials)
            {
                string message = "Not Available";
                return message;
            }

            //Exchange Online / On Premise - Impersonation
            if (ExAuth.Impersonate)
            {
                string impersonatedaddress = ExAuth.SmtpAddress;
                return impersonatedaddress;
            }

            //Exchange Online / On Premise - Supplied UserName
            var resolvedNames = ExAuth.EwsService.ResolveName(ExAuth.UserName);
            if (resolvedNames.Count > 0)
            {
                string address = resolvedNames[0].Mailbox.Address;
                return address;
            }

            string notfound = "Email Address Not Found";
            return notfound;
        }

        private Entity GetEmailServerProfile()
        {
            Guid profileid = ((EntityReference)_mailbox["emailserverprofile"]).Id;
            Entity profile = CrmAuth.CrmService.Retrieve("emailserverprofile", profileid, new ColumnSet("incomingcredentialretrieval", "processemailsreceivedafter", "servertype"));
            return profile;
        }

        private void SetErrorStatus()
        {
            mailboxvalueLb.Content = "Error. Check configuration and try again.";
            autopromotevalueLbl.Content = "Error";
            deliverymethodvalueLbl.Content = "Error";
            organizationvalueLbl.Content = "Error";
            smartmatchinvalueLbl.Content = "Error";
            trackingtokenvalueLbl.Content = "Error";
            getEmailBtn.IsEnabled = false;
            errorBtn.Visibility = Visibility.Visible;
        }

        private Entity GetQueueSettings(Entity mailbox)
        {
            EntityReference queue = (EntityReference)mailbox["regardingobjectid"];
            Guid queueid = queue.Id;

            QueryByAttribute query = new QueryByAttribute("queue");
            query.Attributes.AddRange("queueid");
            query.Values.AddRange(queueid);
            query.ColumnSet = new ColumnSet("incomingemailfilteringmethod");

            EntityCollection queuecollection = CrmAuth.CrmService.RetrieveMultiple(query);
            Entity queuesetting = queuecollection[0];
            return queuesetting;
        }

        private void SetPropertyValuesFromMetadata(Entity mailbox, Entity organization, Entity userorqueue)
        {
            OptionSetValue filteringsetting = (OptionSetValue)userorqueue["incomingemailfilteringmethod"];
            DeliveryMethod = (OptionSetValue)mailbox["incomingemaildeliverymethod"];
            ConnectionChannel = (OptionSetValue)organization["emailconnectionchannel"];
            if (CrmAuth.IsQueue)
            {
                FilteringMethod = GetOptionSetValueLabel("queue", "incomingemailfilteringmethod", filteringsetting.Value, CrmAuth.CrmService);
            }
            else
            {
                FilteringMethod = GetOptionSetValueLabel("usersettings", "incomingemailfilteringmethod", filteringsetting.Value, CrmAuth.CrmService);
            }
            DeliveryMethodString = DetermineDeliveryMethod(DeliveryMethod.Value);
              
        }

        private string DetermineDeliveryMethod(int value)
        {
            string method = "undefined";

            if (value == 2)
            {
                method = GetOptionSetValueLabel("organization", "emailconnectionchannel", ConnectionChannel.Value, CrmAuth.CrmService);
            }
            else
            {
                method = GetOptionSetValueLabel("mailbox", "incomingemaildeliverymethod", value, CrmAuth.CrmService);
            }

            return method;
        }

        public static string GetOptionSetValueLabel(string entityName, string fieldName, int optionSetValue, IOrganizationService service)
        {

            var attReq = new RetrieveAttributeRequest();
            attReq.EntityLogicalName = entityName;
            attReq.LogicalName = fieldName;
            attReq.RetrieveAsIfPublished = true;

            var attResponse = (RetrieveAttributeResponse)service.Execute(attReq);
            var attMetadata = (EnumAttributeMetadata)attResponse.AttributeMetadata;

            return attMetadata.OptionSet.Options.Where(x => x.Value == optionSetValue).FirstOrDefault().Label.UserLocalizedLabel.Label;

        }

        private Entity GetUserSettings(OrganizationServiceProxy crmService, Guid userid)
        {
            EntityReference systemuser = new EntityReference("systemuser", userid);
            QueryByAttribute query = new QueryByAttribute("usersettings");
            query.Attributes.AddRange("systemuserid");
            query.Values.AddRange(userid);
            query.ColumnSet = new ColumnSet("incomingemailfilteringmethod");

            EntityCollection usersettings = crmService.RetrieveMultiple(query);
            Entity usersetting = usersettings[0];
            return usersetting;
        }

        private Entity GetOrganization(crmauthinfo crmAuth)
        {
            Entity org = crmAuth.CrmService.Retrieve("organization", crmAuth.OrgDetail.OrganizationId, new ColumnSet("enablesmartmatching", "maximumtrackingnumber", "emailconnectionchannel", "orgdborgsettings", "emailcorrelationenabled", "ignoreinternalemail"));
            return org;
        }

        private Entity GetMailbox(Guid userid)
        {
            QueryByAttribute query = new QueryByAttribute("mailbox");
            query.ColumnSet = new ColumnSet("incomingemaildeliverymethod", "emailaddress", "incomingemailstatus", "enabledforincomingemail", "postponemailboxprocessinguntil", "processingstatecode", "orgmarkedasprimaryforexchangesync", "emailrouteraccessapproval", "isemailaddressapprovedbyo365admin", "receivingpostponeduntil", "allowemailconnectortousecredentials", "emailserverprofile", "username");
            query.Attributes.AddRange("regardingobjectid");
            query.Values.AddRange(userid);
            query.Attributes.AddRange("statecode");
            query.Values.AddRange(0);

            EntityCollection mailboxes = CrmAuth.CrmService.RetrieveMultiple(query);
            Entity mailbox = mailboxes[0];
            return mailbox;
        }


        //Overload for Queues
        private Entity GetMailbox(string queueAddress)
        {
            QueryByAttribute query = new QueryByAttribute("mailbox");
            query.ColumnSet = new ColumnSet("incomingemaildeliverymethod", "emailaddress", "regardingobjectid", "incomingemailstatus", "enabledforincomingemail", "postponemailboxprocessinguntil", "processingstatecode", "orgmarkedasprimaryforexchangesync", "emailrouteraccessapproval", "isemailaddressapprovedbyo365admin", "receivingpostponeduntil", "allowemailconnectortousecredentials", "emailserverprofile", "username");
            query.Attributes.AddRange("emailaddress");
            query.Values.AddRange(queueAddress);
            query.Attributes.AddRange("statecode");
            query.Values.AddRange(0);

            EntityCollection mailboxes = CrmAuth.CrmService.RetrieveMultiple(query);

            if (mailboxes.Entities.Count != 1)
            {
                throw new InvalidOperationException("Found more or less than 1 active mailbox matching the provided queue mailbox address. Please correct the mailbox configuration and try again.");
            }
            Entity mailbox = mailboxes[0];
            CheckRegarding(mailbox);

            return mailbox;
        }

        private void CheckRegarding(Entity mailbox)
        {
            EntityReference regarding = (EntityReference)mailbox["regardingobjectid"];
            if (regarding.LogicalName != "queue")
            {
                throw new InvalidOperationException("The supplied e-mail address does not belong to a queue. Please correct the mailbox configuration and try again.");
            }
        }

        private Guid DoWhoAmI(OrganizationServiceProxy crmService)
        {
            WhoAmIRequest whoamireq = new WhoAmIRequest();
            WhoAmIResponse whoamiresp = (WhoAmIResponse)crmService.Execute(whoamireq);
            Guid userid = whoamiresp.UserId;
            return userid;
        }

        private void getEmailBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (receivedDateTxtBox.SelectedDate == null)
                {
                    MessageBox.Show("Please select a valid date");
                    return;
                }
                SearchFilter filter = GetSearchFilter();
                ItemView view = new ItemView(20);
                view.OrderBy.Add(ItemSchema.DateTimeReceived, SortDirection.Descending);
                view.PropertySet = ExchangeAuth.FindItemsProps;
                FindItemsResults<Item> result = ExchangeAuth.EwsService.FindItems(WellKnownFolderName.Inbox, filter, view);
                List<FindEmailResult> emails = ConvertFoundItems(result);
                PopulateGrid(emails);
                validateBtn.IsEnabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(MainWindow.GetErrorDetail(ex), "Error retrieving email");
            }
        }

        private List<FindEmailResult> ConvertFoundItems(FindItemsResults<Item> result)
        {
            List<FindEmailResult> emails = new List<FindEmailResult>();

            foreach (var item in result)
            {
                FindEmailResult email = new FindEmailResult();
                email.DateReceived = item.DateTimeReceived;
                email.Id = item.Id;
                email.Subject = item.Subject;

                if (item.ExtendedProperties.Count == 2)
                {
                    email.crmLinkState = (double)item.ExtendedProperties[0].Value;
                    email.crmid = (string)item.ExtendedProperties[1].Value;
                }
                else
                {
                    email.crmLinkState = null;
                }

                emails.Add(email);
            }

            return emails;
        }

        private void PopulateGrid(List<FindEmailResult> emails)
        {
   
            emailGrid.ItemsSource = emails;
            
        }

        private SearchFilter GetSearchFilter()
        {
            List<SearchFilter> searchfiltercollection = new List<SearchFilter>();
            searchfiltercollection.Add(new SearchFilter.IsEqualTo(EmailMessageSchema.From, senderTxtBox.Text));
            searchfiltercollection.Add(new SearchFilter.IsGreaterThan(EmailMessageSchema.DateTimeReceived, receivedDateTxtBox.SelectedDate));

            SearchFilter searchFilter = new SearchFilter.SearchFilterCollection(Microsoft.Exchange.WebServices.Data.LogicalOperator.And, searchfiltercollection.ToArray());
            return searchFilter;
        }

        private EmailMessage BindToItem(FindEmailResult item)
        {
            EmailMessage email = EmailMessage.Bind(ExchangeAuth.EwsService, item.Id);
            return email; 
           
        }

        private void emailGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }

        private void validateBtn_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                if (emailGrid.Items.CurrentItem == null)
                {
                    MessageBox.Show("Please select an email and try again.");
                    return;
                }
                EmailMessage email = BindToItem((FindEmailResult)(emailGrid.Items.CurrentItem));
                bool useextraproperties = DetermineProperties();
                //CheckIncomingEmailResponse emailresponse = CheckEmail(email, useextraproperties);
                var resultwindow = new ResultWindow(CrmAuth, ExchangeAuth, email.Subject, email, useextraproperties) { Owner = this };
                resultwindow.Show();
            }
            catch (Exception ex)
            {
                MessageBox.Show(MainWindow.GetErrorDetail(ex), "Error During Validation");
            }
        }

        private bool DetermineProperties()
        {
            if (ConnectionChannel.Value == 0 && DeliveryMethod.Value == 2  || DeliveryMethod.Value == 3)
            {
                return true;
            }
            return false; 
        }

        //private CheckIncomingEmailResponse CheckEmail(EmailMessage email, bool UseExtraProperties)
        //{
        //        CheckIncomingEmailRequest checkrequest = new CheckIncomingEmailRequest();
        //        checkrequest.Bcc = GetAddresses(email.BccRecipients);
        //        checkrequest.Cc = GetAddresses(email.CcRecipients);
        //        checkrequest.From = email.From.Address;
        //        checkrequest.MessageId = email.InternetMessageId;
        //        checkrequest.Subject = email.Subject;
        //        checkrequest.To = GetAddresses(email.ToRecipients);
        //        if (UseExtraProperties)
        //        {
        //            checkrequest.ExtraProperties = ConversationIndexUtility.GetExtraProperties(email);
        //        }

        //        //Accounting for race condition...
        //        if (CrmAuth.CrmService == null)
        //        {
        //            CrmAuth.GetOrgProxy(CrmAuth.OrgDetail);
        //        }

        //        CheckIncomingEmailResponse checkresponse = (CheckIncomingEmailResponse)CrmAuth.CrmService.Execute(checkrequest);
        //        return checkresponse;
        //}

        //private string GetAddresses(EmailAddressCollection recipients)
        //{
        //    StringBuilder addresses = new StringBuilder();
        //    foreach (var recipient in recipients)
        //    {
        //        addresses.Append(recipient.Address);
        //        addresses.Append(";");
        //    }
        //    return addresses.ToString();
        //}

        private void errorBtn_Click(object sender, RoutedEventArgs e)
        {
            MessageBox.Show(MainWindow.GetErrorDetail(Exception), "Error Details");
        }

        private void btnDiagnostic_Click(object sender, RoutedEventArgs e)
        {
            var WindowDiag = new DiagnosticWindow(_organization, _mailbox, _queue, _userSetting, _emailServerProfile, CrmAuth, ExchangeAuth) { Owner = this };
            WindowDiag.Show();
        }
    }

   
}
